﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;

namespace OxyPlot.CatExtension
{
    internal static class SIMD
    {
        public static (T, T) MinMax<T>(Span<T> span) where T : unmanaged, INumber<T>
        {
            if (Vector<T>.IsSupported && span.Length >= Vector<T>.Count * 2)
            {
                ref var start = ref MemoryMarshal.GetReference(span);
                var minVector = Vector.LoadUnsafe(ref start);
                var maxVector = Vector.LoadUnsafe(ref start);
                start = ref Unsafe.Add(ref start, Vector<T>.Count);

                for (int i = 1; i < span.Length / Vector<T>.Count; i++)
                {
                    var vector = Vector.LoadUnsafe(ref start);
                    minVector = Vector.Min(minVector, vector);
                    maxVector = Vector.Max(maxVector, vector);

                    start = ref Unsafe.Add(ref start, Vector<T>.Count);
                }

                var min = minVector[0];
                var max = maxVector[0];

                for (int i = 1; i < Vector<T>.Count; i++)
                {
                    min = T.Min(min, minVector[i]);
                    max = T.Max(max, maxVector[i]);
                }

                for (int i = 0; i < span.Length % Vector<T>.Count; i++)
                {
                    var v = start;

                    min = T.Min(min, v);
                    max = T.Max(max, v);

                    start = ref Unsafe.Add(ref start, 1);
                }
                return (min, max);
            }
            else
            {
                var min = span[0];
                var max = span[0];
                for (int i = 1; i < span.Length; i++)
                {
                    var v = span[i];
                    max = T.Max(max, v);
                    min = T.Min(min, v);
                }
                return (min, max);
            }
        }
    }
}
